﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;

namespace Nature.Data
{

    

    public class GenericThermo7 : IThermo7
    {
        public string SpeciesID
        {
            get;
            set;
        }

        public ReadOnlyArray<Thermo7Range> Ranges
        {
            get;
            set;
        }
    }

    public partial class AtomsPerMoleculeAmount
    {
        public ChemicalElement Element { get; internal set; }

        public ChemicalSpecies Species { get; internal set; }
    }

    public class GenericAtomsPerMoleculeAmount : IAtomsPerMoleculeAmount
    {
        public string SpeciesID
        {
            get;
            set;
        }

        public string ElementID
        {
            get;
            set;
        }

        public double AtomsPerMolecule
        {
            get;
            set;
        }
    }


    [DebuggerDisplay("{SpeciesID}")]
    public partial class ChemicalSpecies
    {
        public int Ordinal
        {
            get;
            internal set;
        }

        public ReadOnlyArray<AtomsPerMoleculeAmount> AtomsPerMoleculeAmounts { get; internal set; }

        public ReadOnlyArray<ChemicalReaction> Reactions { get; internal set; }


    }

    [DebuggerDisplay("{ElementID}")]
    public partial class ChemicalElement
    {        
        public ReadOnlyArray<AtomsPerMoleculeAmount> AtomsPerMoleculeAmounts { get; internal set; }

        public ReadOnlyArray<ChemicalReaction> Reactions { get; internal set; }
    }

    [DebuggerDisplay("{AtomsPerMolecule} atoms of {ElementID} in {SpeciesID}")]
    public partial class AtomsPerMoleculeAmount { }


    public partial struct Thermo7Range
    {
        public Thermo7Range(double tmin, double tmax,
            double a1, double a2, double a3, double a4, double a5, 
            double a6, double a7) : this()
        {
            this.Tmin = tmin;
            this.Tmax = tmax;
            this.A1 = a1;
            this.A2 = a2;
            this.A3 = a3;
            this.A4 = a4;
            this.A5 = a5;
            this.A6 = a6;
            this.A7 = a7;
            OnCreated();
        }
    }

    public partial class Thermo7
    {       
        public ChemicalSpecies Species { get; internal set; }
    }

    public partial class GasKinParameters
    {      

        public ChemicalSpecies Species { get; internal set; }

        public bool IsPolar { get { return this.Alpha != 0.0; } }

        /// <summary>
        /// Rotational relaxation collision factor F(T) 
        /// </summary>
        public double RotRelCollFactor(double temperature)
        {
            double f;
            double re = this.Epsilon / temperature;
            double sqrt_re = Math.Sqrt(re);
            double pi_3_2 = 5.568327996831708;
            f = 1.0;
            f += 0.5 * pi_3_2 * sqrt_re;
            f += (0.25 * Math.PI * Math.PI + 2.0) * re;
            f += pi_3_2 * re * sqrt_re;
            return (f);
        }

        internal GasKinParameters(GasKinParameters v1, GasKinParameters v2)
        {
            Epsilon = Math.Sqrt(v1.Epsilon * v2.Epsilon);
            Sigma = 0.5 * (v1.Sigma + v2.Sigma);
            if (v1.IsPolar == v2.IsPolar)
            {
                Mu = Math.Sqrt(v1.Mu * v2.Mu);
            }
            else
            {
                GasKinParameters n = v1, p = v2;
                if (v1.IsPolar) { n = v2; p = v1; }
                double ksy = 1.0 + 0.25 * n.Alpha * p.Mu * Math.Sqrt(p.Epsilon / n.Epsilon);
                Mu = 0.0;
                Epsilon *= (ksy * ksy);
                Sigma /= Math.Pow(ksy, 1.0 / 6.0);
            }
        }
    }

    [DebuggerDisplay("{SpeciesID} {Coefficient}")]
    public partial class Enhanced
    {
        public ChemicalSpecies Species { get; internal set; }
    }

    [DebuggerDisplay("A={A}, b={B}, E={C}")]
    public partial class ArrheniusParameters
    {
        public bool IsConstant
        {
            get { return this.B == 0d && this.C == 0d; }
        }
    }

    [DebuggerDisplay("{SpeciesID}")]
    public partial class ChemicalReagent
    {
        public ChemicalSpecies Species { get; internal set; }
    }

    public partial class ChemicalReaction
    {
        public int Ordinal
        {
            get;
            internal set;
        }

        public ReadOnlyArray<ChemicalElement> Elements { get; internal set; }

        public ReadOnlyArray<ChemicalSpecies> Species { get; internal set; }

        public ReadOnlyArray<ChemicalSpecies> SpeciesDependencies { get; internal set; }

        public double MolesPerActProduction
        {
            get 
            {
                return Reagents.Sum(r=> r.RevCoefficient - r.FwdCoefficient);
            }
        }

        public double SpeciesProductionPerAct(string speciesID)
        {
            return Reagents.Where(i => i.SpeciesID == speciesID).Sum(i=> i.RevCoefficient - i.FwdCoefficient);
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();            
            int counter;
            counter = 0;
            foreach (var reagent in this.Reagents.Where(i=> i.FwdCoefficient > 0d))
            {
                sb.AppendFormat("{0}{1}{2}", counter++ > 0 ? "+" : string.Empty,
                    reagent.FwdCoefficient != 1d ? reagent.FwdCoefficient.ToString() : string.Empty, 
                    reagent.SpeciesID);
            }
            if (ThirdBodyID != null)
            {
                sb.AppendFormat("(+{0})", ThirdBodyID);
            }
            sb.Append(IsReversible ? "<=>" : "=>");
            counter = 0;
            foreach (var reagent in this.Reagents.Where(i => i.RevCoefficient > 0d))
            {
                sb.AppendFormat("{0}{1}{2}", counter++ > 0 ? "+" : string.Empty,
                    reagent.RevCoefficient != 1d ? reagent.RevCoefficient.ToString() : string.Empty, 
                    reagent.SpeciesID);
            }
            if (ThirdBodyID != null)
            {
                sb.AppendFormat("(+{0})", ThirdBodyID);
            }
            return sb.ToString();
        }
    }

    public interface ISpeciesListDefinition
    {
        IEnumerable<string> SpeciesIDs
        {
            get;
        }
    }

    public class SpeciesListDefinition : ISpeciesListDefinition
    {
        readonly List<string> m_speciesIDs;
        public SpeciesListDefinition(IEnumerable<string> speciesIDs)
        {
            m_speciesIDs = speciesIDs.ToList();
        }


        #region ISpeciesListDefinition Members

        public IEnumerable<string> SpeciesIDs
        {
            get { return new ReadOnlyCollection<string>(m_speciesIDs);  }
        }

        #endregion
    }
}
